﻿using System.Collections.Specialized;
using System.Linq;
using System.Web.Security;

using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenFaq.Core;
using OpenFaq.Core.UserModule;

namespace OpenFaq.Application.Tests
{
	[TestClass]
	public class CustomMembershipProviderTest : TestBase
	{
		CustomMembershipProvider _membershipProvider;

		[TestInitialize()]
		public void MyTestInitialize()
		{
			_membershipProvider = new CustomMembershipProvider();
		}


		[TestMethod]
		public void CreateUser_GivenDefaultMembershipProviderSettings_WhenValidData_ReturnsUser()
		{
			//Initialize with defaults.
			InitMembershipProviderWithDefaultSettings();

			MembershipUser user = CreateValidUser();
			Assert.IsNotNull(user);
		}

		
		private MembershipUser CreateValidUser()
		{
			MembershipCreateStatus status;
			return _membershipProvider.CreateUser("user_1", "pass_1", "user_1@test.com", "question", "answer", true,
												  null, out status);
		}

		[TestMethod]
		public void CreateUser_GivenPasswordRequiresMin6Chars_WhenPasswordHasLessThen6Chars_ReturnsNullAndStatusInvalidPassword()
		{
			InitMembershipProviderWithNoValidation("minRequiredPasswordLength", "6");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "only5", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNull(user);
			Assert.AreEqual(MembershipCreateStatus.InvalidPassword, status);
		}



		[TestMethod]
		public void CreateUser_GivenPasswordRequiringMin6Chars_WhenPasswordHasMoreThen6Chars_ReturnsUser()
		{
			InitMembershipProviderWithNoValidation("minRequiredPasswordLength", "6");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "more_then_6", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNotNull(user);
		}

		[TestMethod]
		public void CreateUser_GivenPasswordRequiresOneNonAphaNumericCharacter_WhenPasswordHavsNoNonAlphaNumericCharacters_ReturnsNullAndStatusInvalidPassword()
		{
			InitMembershipProviderWithNoValidation("minRequiredNonAlphanumericCharacters", "1");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "nononalphanumeric", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNull(user);
			Assert.AreEqual(MembershipCreateStatus.InvalidPassword, status);
		}

		[TestMethod]
		public void CreateUser_GivenPasswordRequiresOneAphaNumericCharacter_WhenPasswordHasOneAlphaNumericCharacter_ReturnsUser()
		{
			InitMembershipProviderWithNoValidation("minRequiredNonAlphanumericCharacters", "1");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "hasnonalphanumeric_1", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNotNull(user);
		}



		[TestMethod]
		public void CreateUser_GivenPasswordRequiresMatchRegularExpressions_WhenPassswordNotMatchingRegularExpreation_ReturnsNullAndStatusInvalidPassword()
		{
			
			InitMembershipProviderWithNoValidation("passwordStrengthRegularExpression", "^start");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "nomatchregexp_1", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNull(user);
			Assert.AreEqual(MembershipCreateStatus.InvalidPassword, status);
		}


		[TestMethod]
		public void CreateUser_GivenPasswordRequiresMatchRegularExpressions_WhenPassswordMatchingRegularExpreation_ReturnsNullAndStatusInvalidPassword()
		{
			InitMembershipProviderWithNoValidation("passwordStrengthRegularExpression", "^start");

			MembershipCreateStatus status;
			MembershipUser user = _membershipProvider.CreateUser("user_1", "start1", "user_1@test.com", "question", "answer", true,
												  null, out status);
			Assert.IsNotNull(user);
		}


		private void InitMembershipProviderWithDefaultSettings()
		{
			_membershipProvider.Initialize("CustomMembershipProvider", new NameValueCollection());
		}

		

		private void InitMembershipProviderWithNoValidation()
		{
			NameValueCollection config = GetConfigWithNoValidation();

			_membershipProvider.Initialize("CustomMembershipProvider", config);
		}


		private void InitMembershipProviderWithNoValidation(string name, string value)
		{
			NameValueCollection config = GetConfigWithNoValidation();
			config[name] = value;

			InitMembershipProvider(config);
		}

		private NameValueCollection GetConfigWithNoValidation()
		{
			var config = new NameValueCollection();
			//set no validation as default

			//Remove password validation
			config["minRequiredNonAlphanumericCharacters"] = "0";
			config["minRequiredPasswordLength"] = "0";
			config["passwordStrengthRegularExpression"] = "";

			config["RequiresQuestionAndAnswer"] = "false";
			config["RequiresUniqueEmail"] = "false";
			return config;
		}


		private void InitMembershipProvider(NameValueCollection config)
		{
			_membershipProvider.Initialize("CustomMembershipProvider", config);
		}


		[TestMethod]
		public void CreateUser_WhenUsernameNotUnique_ReturnsNullAndStatusDuplicateUserName()
		{
			InitMembershipProviderWithNoValidation();

			MembershipCreateStatus status;
			MembershipUser user1 = _membershipProvider.CreateUser("user_1", "start1", "user_1@test.com", null, null, true,
												  null, out status);
			Assert.IsNotNull(user1);

			MembershipUser user2 = _membershipProvider.CreateUser("user_1", "start1", "user_1@test.com", null, null, true,
												  null, out status);
			Assert.IsNull(user2);
			Assert.AreEqual(MembershipCreateStatus.DuplicateUserName, status);
		}

		[TestMethod]
		public void CreateUser_GivenPasswordFormatHashed_HashesPassword()
		{
			InitMembershipProviderWithNoValidation("passwordFormat", "Hashed");

			MembershipCreateStatus status;
			MembershipUser membershipUser = _membershipProvider.CreateUser("user_1", "pass_1", "user_1@test.com", null, null, true,
												  null, out status);
			Assert.IsNotNull(membershipUser);

			User user = GetUserById((int)membershipUser.ProviderUserKey);
			Assert.AreNotEqual("pass_1", user.Password);
		}

		[TestMethod]
		public void ChangePassword_WhenValidArguements_ChangesPassword()
		{
			var config = new NameValueCollection();
			config["passwordFormat"] = "Clear";
			InitMembershipProvider(config);

			MembershipCreateStatus status;
			MembershipUser membershipUser = _membershipProvider.CreateUser("user_1", "pass_1", "user_1@test.com", "question", "answer", true,
												 null, out status);

			bool success = _membershipProvider.ChangePassword("user_1", "pass_1", "pass_2");
			Assert.IsTrue(success);

			User user = GetUserById((int)membershipUser.ProviderUserKey);
			Assert.AreEqual("pass_2", user.Password);
		}


		[TestMethod]
		public void ValidateUser_WhenUserWithUsernameAndPasswordExists_ReturnsTrue()
		{
			InitMembershipProviderWithNoValidation();

			MembershipCreateStatus status;
			MembershipUser membershipUser = _membershipProvider.CreateUser("user_1", "pass_1", "user_1@test.com", null, null, true,
												 null, out status);
			Assert.IsNotNull(membershipUser);

			bool success = _membershipProvider.ValidateUser("user_1", "pass_1");
			Assert.IsTrue(success);
		}

		[TestMethod]
		public void ValidateUser_WhenUserWithUsernameAndPasswordDoesNotExist_ReturnsFalse()
		{
			InitMembershipProviderWithNoValidation();

			bool success = _membershipProvider.ValidateUser("user_1", "pass_1");
			Assert.IsFalse(success);
		}

		private User GetUserById(int userId)
		{
			User user;
			using (var db = new OpenFaqContext())
			{
				user = db.Users.Single(u => u.Id == userId);
			}
			return user;
		}
	}
}
